package com.atguigu.service.impl;

import com.atguigu.constant.RedisConst;
import com.atguigu.entity.PrepareSeckillOrder;
import com.atguigu.entity.SeckillProduct;
import com.atguigu.entity.UserSeckillSkuInfo;
import com.atguigu.mapper.SeckillProductMapper;
import com.atguigu.result.RetVal;
import com.atguigu.result.RetValCodeEnum;
import com.atguigu.service.SeckillProductService;
import com.atguigu.util.MD5;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author GuoJH
 * @since 2021-09-13
 */
@Service
public class SeckillProductServiceImpl extends ServiceImpl<SeckillProductMapper, SeckillProduct> implements SeckillProductService {
    @Autowired
    private RedisTemplate redisTemplate;
    //2.秒杀商品的详情信息  根据skuId获取秒杀商品
    @Override
    public SeckillProduct getSeckillProductBySkuId(Long skuId) {
        SeckillProduct seckillProduct = (SeckillProduct)redisTemplate.opsForHash().get(RedisConst.SECKILL_PRODUCT, skuId.toString());
        return seckillProduct;
    }

    @Override
    public void prepareSeckill(UserSeckillSkuInfo userSeckillSkuInfo) {
        Long skuId = userSeckillSkuInfo.getSkuId();
        String userId = userSeckillSkuInfo.getUserId();
        //判断商品是否 卖完了
        String status = (String)redisTemplate.opsForValue().get(RedisConst.SECKILL_STATE_PREFIX+skuId);
        if (RedisConst.CAN_NOT_SECKILL.equals(status)){
            //代表卖完了  直接返回
            return;
        }
        //把用户抢购的商品放入缓存 (缓存中没有时候在往里面放)   判断该用户是否抢购过
        boolean flag = redisTemplate.opsForValue().setIfAbsent(RedisConst.PREPARE_SECKILL_USERID_SKUID+userId,skuId,RedisConst.PREPARE_SECKILL_LOCK_TIME, TimeUnit.SECONDS);
        if (!flag){
            //如果没添加进去  则证明里面有缓存了
            return;
        }
        //flag 为true 则代表添加成功下单成功 redis中减掉一个库存(减出来的是个skuId)
        String redisSkuId = (String)redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX+skuId).rightPop();
        //如果该库存没有了，通知其他节点该商品不能秒杀
        if (StringUtils.isEmpty(redisSkuId)){
            //将商品的状态改为0  不能进行秒杀了
            redisTemplate.convertAndSend(RedisConst.PREPARE_PUB_SUB_SECKILL,skuId+":"+ RedisConst.CAN_NOT_SECKILL);
            return;
        }
        //把用户抢购的预下单商品信息存储到redis
        PrepareSeckillOrder prepareSeckillOrder = new PrepareSeckillOrder();
        prepareSeckillOrder.setUserId(userId);
        prepareSeckillOrder.setBuyNum(1);
        SeckillProduct seckillProduct = getSeckillProductBySkuId(skuId);
        prepareSeckillOrder.setSeckillProduct(seckillProduct);
        //设置订单码
        prepareSeckillOrder.setPrepareOrderCode(MD5.encrypt(userId+skuId));
        redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).put(userId,prepareSeckillOrder);
        //更新秒杀商品库存信息MySQL+Redis
        updateSeckillStock(skuId);
    }
    //5.判断用户是否有抢购的资格
    @Override
    public RetVal hasQualified(Long skuId, String userId) {
        //判断预订单中是否有用户信息
        boolean flag = redisTemplate.hasKey(RedisConst.PREPARE_SECKILL_USERID_SKUID+userId);
        if (flag){
            //再判断是否有用户的订单信息
            PrepareSeckillOrder prepareSeckillOrder = (PrepareSeckillOrder)redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).get(userId);
            if (prepareSeckillOrder != null){
                //有订单信息  代表抢单成功
                return RetVal.build(prepareSeckillOrder, RetValCodeEnum.PREPARE_SECKILL_SUCCESS);
            }
        }
        //如过用户已经秒杀了该商品 则返回抢购成功
        Integer orderId =(Integer) redisTemplate.boundHashOps(RedisConst.BOUGHT_SECKILL_USER_ORDER).get(userId);
        if(orderId!=null){
            return RetVal.build(orderId,RetValCodeEnum.SECKILL_ORDER_SUCCESS);
        }
        return RetVal.build(null,RetValCodeEnum.SECKILL_RUN);
    }

    //更新秒杀商品库存信息MySQL+Redis
    private void updateSeckillStock(Long skuId) {
        //先获取redis中库存剩余数量
        Long size = redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId).size();
        //更新数据库  这里设置的是每两次更新一次
        if (size%2==0){
            SeckillProduct seckillProduct = getSeckillProductBySkuId(skuId);
            seckillProduct.setStockCount(size.intValue());
            baseMapper.updateById(seckillProduct);
            //在更新redis  为了在页面显示还有多少个
            redisTemplate.boundHashOps(RedisConst.SECKILL_PRODUCT).put(skuId,seckillProduct);
        }
    }
}
